package com.kawins.supers.table;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.kawins.base.BaseService;
import com.kawins.result.ResultTo;
import com.kawins.supers.table.core.TableCode;
import com.kawins.supers.table.manager.TableMethod;
import com.kawins.util.web.RequestUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tk.mybatis.mapper.entity.Example;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author rankai
 *         createTime 2017-04-2017/4/27 21:04
 */
public class DataTablePage {

    private static final Logger log = LoggerFactory.getLogger(DataTablePage.class);

    private int start = 0;

    private int length = 10;

    private String orderBy;

    private Class clazz;

    private BaseService baseService;

    private HttpServletRequest request;

    public DataTablePage(BaseService baseService, Class clazz) {
        this.request = RequestUtil.getRequest();
        String start = this.request.getParameter(TableCode.PAGE_START_NAME);
        String length = this.request.getParameter(TableCode.PAGE_LENGTH_NAME);
        orderBy = this.request.getParameter(TableCode.PAGE_ORDER_NAME);
        this.start = start != null ? Integer.parseInt(start) : this.start;
        this.length = length != null ? Integer.parseInt(length) : this.length;
        this.baseService = baseService;
        this.clazz = clazz;
    }

    /**
     * 分页Base方法
     *
     * @return
     */
    public ResultTo pageList() {
        Example example = buildExample(null, null, null);
        PageHelper.startPage(this.start, this.length);
        List list = this.baseService.selectByExample(example);
        return new ResultTo().setData(new PageInfo(list));
    }

    /**
     * 分页Base方法,排除exception中的搜索Key
     *
     * @param exception
     * @return
     */
    public ResultTo pageList(String[] exception) {
        Example example = buildExample(null, null, exception);
        PageHelper.startPage(this.start, this.length);
        List list = this.baseService.selectByExample(example);
        return new ResultTo().setData(new PageInfo(list));
    }

    /**
     * 增加map参数
     *
     * @param parameterMap
     * @return
     */
    public ResultTo pageList(Map<String, Object> parameterMap) {
        Example example = buildExample(null, parameterMap, null);
        PageHelper.startPage(this.start, this.length);
        List list = this.baseService.selectByExample(example);
        return new ResultTo().setData(new PageInfo(list));
    }

    /**
     * 增加map参数,排除exception中的搜索Key
     *
     * @param parameterMap
     * @param exception
     * @return
     */
    public ResultTo pageList(Map<String, Object> parameterMap, String[] exception) {
        Example example = buildExample(null, parameterMap, exception);
        PageHelper.startPage(this.start, this.length);
        List list = this.baseService.selectByExample(example);
        return new ResultTo().setData(new PageInfo(list));
    }

    /**
     * 自定义Example对象
     *
     * @param example
     * @return
     */
    public ResultTo pageList(Example example) {
        example = buildExample(example, null, null);
        PageHelper.startPage(this.start, this.length);
        List list = this.baseService.selectByExample(example);
        return new ResultTo().setData(new PageInfo(list));
    }

    /**
     * 自定义Example对象,增加map参数
     *
     * @param example
     * @return
     */
    public ResultTo pageList(Example example, Map<String, Object> parameterMap) {
        example = buildExample(example, parameterMap, null);
        PageHelper.startPage(this.start, this.length);
        List list = this.baseService.selectByExample(example);
        return new ResultTo().setData(new PageInfo(list));
    }

    /**
     * 自定义Example对象,增加map参数,排除exception中的搜索Key
     *
     * @param example
     * @return
     */
    public ResultTo pageList(Example example, Map<String, Object> parameterMap, String[] exception) {
        example = buildExample(example, parameterMap, exception);
        PageHelper.startPage(this.start, this.length);
        List list = this.baseService.selectByExample(example);
        return new ResultTo().setData(new PageInfo(list));
    }

    /**
     * 自定义分页service,无自定义参数
     *
     * @param methodName
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public ResultTo pageList(String methodName) throws InvocationTargetException, IllegalAccessException {
        return pageList(methodName, null);
    }

    /**
     * 自定义分页service,带自定义参数
     *
     * @param methodName
     * @param parameterMap
     * @return
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public ResultTo pageList(String methodName, Map<String, Object> parameterMap) throws InvocationTargetException, IllegalAccessException {
        Map<String, Object> map = requestToMap(parameterMap, true, null);
        PageHelper.startPage(this.start, this.length);
        Method method = TableMethod.get(methodName);
        List list = null;
        if (method != null) {
            list = (List) method.invoke(baseService, map);
        } else {
            log.warn("在DataPage中没有找到[" + methodName + "]对应的方法");
        }
        return new ResultTo().setData(new PageInfo(list));
    }

    /**
     * 构建Example对象,传入的对象值后加#代表为模糊查询
     *
     * @param example
     * @param parameterMap
     * @return
     */
    private Example buildExample(Example example, Map<String, Object> parameterMap, String[] exception) {
        if (example == null) {
            example = new Example(this.clazz);
        }
        Map<String, Object> map = requestToMap(parameterMap, false, exception);
        Example.Criteria criteria = example.createCriteria();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String value = entry.getValue().toString();
            char charAt = value.charAt(value.length() - 1);
            if (charAt == '#') {
                criteria.andLike(entry.getKey(), "%" + value.substring(0, value.length() - 1) + "%");
            } else {
                criteria.andEqualTo(entry.getKey(), entry.getValue());
            }
        }
        if (StringUtils.isNotBlank(this.orderBy)) {
            char charAtOrder = orderBy.charAt(orderBy.length() - 1);
            if (charAtOrder == '#') {
                example.orderBy(orderBy.substring(0, orderBy.length() - 1)).desc();
            } else {
                example.orderBy(orderBy).asc();
            }
        }
        return example;
    }

    /**
     * 将request参数转化为Map
     *
     * @param parameterMap
     * @return
     */
    private Map<String, Object> requestToMap(Map<String, Object> parameterMap, boolean isMethod, String[] exception) {
        Enumeration<String> parameterNames = this.request.getParameterNames();
        Map<String, Object> map = new HashMap<>();
        while (parameterNames.hasMoreElements()) {
            String s = parameterNames.nextElement();
            if (StringUtils.isBlank(s) || TableCode.PAGE_START_NAME.equals(s) ||
                    TableCode.PAGE_LENGTH_NAME.equals(s) || TableCode.PAGE_ORDER_NAME.equals(s) ||
                    (exception != null && Arrays.asList(exception).contains(s))) {
                continue;
            }
            String parameter = this.request.getParameter(s);
            if (StringUtils.isNotBlank(parameter)) {
                log.debug("收到DateTale分页带搜索请求,搜索参数名:[" + s + "],搜索参数值:[" + parameter + "]");
                if (isMethod) { //带方法名的,不支持加#号
                    char charAt = parameter.charAt(parameter.length() - 1);
                    if (charAt == '#') {
                        map.put(s, parameter.substring(0, parameter.length() - 1));
                        continue;
                    }
                }
                map.put(s, parameter);
            }
        }
        if (parameterMap != null) {
            map.putAll(parameterMap);
        }
        return map;
    }
}
